# Copyright 2020 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools.float_utils import float_compare, float_is_zero, float_round
from odoo.exceptions import UserError
from collections import defaultdict

class StockMove(models.Model):
    _inherit = "stock.move"
    # reserved_availability = fields.Float(
    #     'Quantity Reserved', compute='_compute_reserved_availability',
    #     digits='Product Unit of Measure',
    #     readonly=True, help='Quantity that has already been reserved for this move')
    # quantity_done = fields.Float(
    #     'Quantity Done', compute='_quantity_done_compute', digits='Product Unit of Measure',
    #     inverse='_quantity_done_set', store=True)
    # move_line_nosuggest_ids = fields.One2many('stock.move.line', 'move_id',
    #                                           domain=['|', ('quantity', '=', 0.0), ('qty_done', '!=', 0.0)])
    # # RMAs that were created from the delivery move
    # rma_ids = fields.One2many(
    #     comodel_name="rma",
    #     inverse_name="move_id",
    #     string="RMAs",
    #     copy=False,
    # )
    # # RMAs linked to the incoming movement from client
    # rma_receiver_ids = fields.One2many(
    #     comodel_name="rma",
    #     inverse_name="reception_move_id",
    #     string="RMA receivers",
    #     copy=False,
    # )
    # RMA that create the delivery movement to the customer
    s_rma_id = fields.Many2one("rma", string="RMA return", readonly=True)

    # def _get_move_lines(self):
    #     """ This will return the move lines to consider when applying _quantity_done_compute on a stock.move.
    #     In some context, such as MRP, it is necessary to compute quantity_done on filtered sock.move.line."""
    #     self.ensure_one()
    #     if self.picking_type_id.show_reserved is False:
    #         return self.move_line_nosuggest_ids
    #     return self.move_line_ids
    #
    # def _quantity_done_sml(self):
    #     lines = self._get_move_lines()
    #     q = 0
    #     for l in lines:
    #         q = l.qty_done+q
    #     return q
    #
    # @api.depends('move_line_ids.qty_done', 'move_line_ids.product_uom_id', 'move_line_nosuggest_ids.qty_done')
    # def _quantity_done_compute(self):
    #     """ This field represents the sum of the move lines `qty_done`. It allows the user to know
    #     if there is still work to do.
    #
    #     We take care of rounding this value at the general decimal precision and not the rounding
    #     of the move's UOM to make sure this value is really close to the real sum, because this
    #     field will be used in `_action_done` in order to know if the move will need a backorder or
    #     an extra move.
    #     """
    #     if not any(self._ids):
    #         # onchange
    #         for move in self:
    #             move.quantity_done = move._quantity_done_sml()
    #     else:
    #         # compute
    #         move_lines_ids = set()
    #         for move in self:
    #             move_lines_ids |= set(move._get_move_lines().ids)
    #
    #         data = self.env['stock.move.line']._read_group(
    #             [('id', 'in', list(move_lines_ids))],
    #             ['move_id', 'product_uom_id'], ['quantity:sum']
    #         )
    #         sum_qty = defaultdict(float)
    #         for move, product_uom, qty_sum in data:
    #             uom = move.product_uom
    #             sum_qty[move.id] += product_uom._compute_quantity(qty_sum, uom, round=False)
    #
    #         for move in self:
    #             move.quantity_done = sum_qty[move.id]
    #
    # def _quantity_done_set(self):
    #     def _process_decrease(move, quantity):
    #         """Prioritize decrease the ml without reserved qty"""
    #         res_mls = move._get_move_lines().sorted(
    #             lambda ml: float_is_zero(ml.reserved_uom_qty, precision_rounding=ml.product_uom_id.rounding),
    #             reverse=True)
    #         qty_to_unreserve = move.reserved_availability - move.product_uom_qty
    #         for ml in res_mls:
    #             if float_is_zero(quantity, precision_rounding=move.product_uom.rounding):
    #                 break
    #             qty_ml_dec = min(ml.qty_done,
    #                              ml.product_uom_id._compute_quantity(quantity, ml.product_uom_id, round=False))
    #             if float_is_zero(qty_ml_dec, precision_rounding=ml.product_uom_id.rounding):
    #                 continue
    #             ml.qty_done -= qty_ml_dec
    #             quantity -= move.product_uom._compute_quantity(qty_ml_dec, move.product_uom, round=False)
    #             if not ml.exists():
    #                 # If decreasing the move line qty_done to 0 let it to be unlinked (i.e. for immediate transfers)
    #                 continue
    #             # Unreserve
    #             immediate_transfer = move.state in ['done']
    #             if (not immediate_transfer and move.reserved_availability < move.product_uom_qty):
    #                 continue
    #             if float_compare(ml.reserved_uom_qty, ml.qty_done, precision_rounding=ml.product_uom_id.rounding) <= 0:
    #                 continue
    #             if immediate_transfer:
    #                 ml.reserved_uom_qty = ml.qty_done
    #             elif float_compare(qty_to_unreserve, 0, precision_rounding=move.product_uom.rounding) > 0:
    #                 qty_unreserved = min(qty_to_unreserve, ml.reserved_qty - ml.qty_done)
    #                 ml.reserved_uom_qty = ml.reserved_qty - qty_unreserved
    #                 qty_to_unreserve -= qty_unreserved
    #
    #     def _process_increase(move, quantity):
    #         moves = move
    #         #if move.picking_id.immediate_transfer:
    #         moves = move._action_confirm(merge=False)
    #         # Kits, already handle in action_explode, should be clean in master
    #         if len(moves) > 1:
    #             return
    #         if move.reserved_availability < move.quantity_done and move.state not in ['done', 'cancel']:
    #             move._action_assign(force_qty=move.quantity_done)
    #         move._set_quantity_done(quantity)
    #
    #     err = []
    #     for move in self:
    #         uom_qty = float_round(move.quantity_done, precision_rounding=move.product_uom.rounding,
    #                               rounding_method='HALF-UP')
    #         precision_digits = self.env['decimal.precision'].precision_get('Product Unit of Measure')
    #         qty = float_round(move.quantity_done, precision_digits=precision_digits, rounding_method='HALF-UP')
    #         if float_compare(uom_qty, qty, precision_digits=precision_digits) != 0:
    #             err.append(_("""
    # The quantity done for the product %s doesn't respect the rounding precision defined on the unit of measure %s.
    # Please change the quantity done or the rounding precision of your unit of measure.""",
    #                          move.product_id.display_name, move.product_uom.display_name))
    #             continue
    #         delta_qty = move.quantity_done - move._quantity_done_sml()
    #         if float_compare(delta_qty, 0, precision_rounding=move.product_uom.rounding) > 0:
    #             _process_increase(move, delta_qty)
    #         elif float_compare(delta_qty, 0, precision_rounding=move.product_uom.rounding) < 0:
    #             _process_decrease(move, abs(delta_qty))
    #     if err:
    #         raise UserError('\n'.join(err))
    #
    # @api.depends('move_line_ids.quantity')
    # def _compute_reserved_availability(self):
    #     """ Fill the `availability` field on a stock move, which is the actual reserved quantity
    #     and is represented by the aggregated `product_qty` on the linked move lines. If the move
    #     is force assigned, the value will be 0.
    #     """
    #     if not any(self._ids):
    #         # onchange
    #         for move in self:
    #             reserved_availability = sum(move.move_line_ids.mapped('quantity'))
    #             move.reserved_availability = move.product_id.uom_id._compute_quantity(
    #                 reserved_availability, move.product_uom, rounding_method='HALF-UP')
    #     else:
    #         # compute
    #         logging.info("begin")
    #         logging.info(self.env['stock.move.line']._read_group([('move_id', 'in', self.ids)],
    #                                                           ['move_id'], ['quantity:sum']))
    #         logging.info("end")
    #         result = {data[0].id: data[1] for data in  #data['move_id'][0]: data['quantity']
    #                   self.env['stock.move.line']._read_group([('move_id', 'in', self.ids)],
    #                                                           ['move_id'], ['quantity:sum'])}#, 'quantity'
    #
    #         #['move_id', 'product_uom_id'], ['quantity:sum']
    #         for move in self:
    #             move.reserved_availability = move.product_id.uom_id._compute_quantity(
    #                 result.get(move.id, 0.0), move.product_uom, rounding_method='HALF-UP')
    #
    # def unlink(self):
    #     # A stock user could have no RMA permissions, so the ids wouldn't
    #     # be accessible due to record rules.
    #     rma_receiver = self.sudo().mapped("rma_receiver_ids")
    #     rma = self.sudo().mapped("rma_id")
    #     res = super().unlink()
    #     rma_receiver.filtered(lambda x: x.state != "cancelled").write(
    #         {"state": "draft"}
    #     )
    #     rma.update_received_state()
    #     rma.update_replaced_state()
    #     return res
    #
    # def _action_cancel(self):
    #     res = super()._action_cancel()
    #     # A stock user could have no RMA permissions, so the ids wouldn't
    #     # be accessible due to record rules.
    #     cancelled_moves = self.filtered(lambda r: r.state == "cancel").sudo()
    #     cancelled_moves.mapped("rma_receiver_ids").write({"state": "draft"})
    #     cancelled_moves.mapped("rma_id").update_received_state()
    #     cancelled_moves.mapped("rma_id").update_replaced_state()
    #     return res
    #
    # def _action_done(self, cancel_backorder=False):
    #     """Avoids to validate stock.move with less quantity than the
    #     quantity in the linked receiver RMA. It also set the appropriated
    #     linked RMA to 'received' or 'delivered'.
    #     """
    #     for move in self.filtered(lambda r: r.state not in ("done", "cancel")):
    #         rma_receiver = move.sudo().rma_receiver_ids
    #         logging.info(move.id)
    #         logging.info(rma_receiver.product_uom_qty)
    #         if rma_receiver and move.quantity_done != rma_receiver.product_uom_qty:
    #             raise ValidationError(
    #                 _(
    #                     "The quantity done for the product '%(id)s' must "
    #                     "be equal to its initial demand because the "
    #                     "stock move is linked to an RMA (%(name)s)."
    #                 )
    #                 % (
    #                     {
    #                         "id": move.product_id.name,
    #                         "name": move.rma_receiver_ids.name,
    #                     }
    #                 )
    #             )
    #     res = super()._action_done(cancel_backorder=cancel_backorder)
    #     move_done = self.filtered(lambda r: r.state == "done").sudo()
    #     # Set RMAs as received. We sudo so we can grant the operation even
    #     # if the stock user has no RMA permissions.
    #     to_be_received = (
    #         move_done.sudo()
    #         .mapped("rma_receiver_ids")
    #         .filtered(lambda r: r.state == "confirmed")
    #     )
    #     to_be_received.update_received_state_on_reception()
    #     # Set RMAs as delivered
    #     move_done.mapped("rma_id").update_replaced_state()
    #     move_done.mapped("rma_id").update_returned_state()
    #     return res
    #
    # @api.model
    # def _prepare_merge_moves_distinct_fields(self):
    #     """The main use is that launched delivery RMAs doesn't merge
    #     two moves if they are linked to a different RMAs.
    #     """
    #     return super()._prepare_merge_moves_distinct_fields() + ["rma_id"]
    #
    # def _prepare_move_split_vals(self, qty):
    #     """Intended to the backport of picking linked to RMAs propagates the
    #     RMA link id.
    #     """
    #     res = super()._prepare_move_split_vals(qty)
    #     res["rma_id"] = self.sudo().rma_id.id
    #     return res
    #
    # def _prepare_return_rma_vals(self, original_picking):
    #     """hook method for preparing an RMA from the 'return picking wizard'."""
    #     self.ensure_one()
    #     partner = original_picking.partner_id
    #     if hasattr(original_picking, "sale_id") and original_picking.sale_id:
    #         partner_invoice_id = original_picking.sale_id.partner_invoice_id.id
    #         partner_shipping_id = original_picking.sale_id.partner_shipping_id.id
    #     else:
    #         partner_invoice_id = partner.address_get(["invoice"]).get("invoice", False)
    #         partner_shipping_id = partner.address_get(["delivery"]).get(
    #             "delivery", False
    #         )
    #     return {
    #         "user_id": self.env.user.id,
    #         "partner_id": partner.id,
    #         "partner_shipping_id": partner_shipping_id,
    #         "partner_invoice_id": partner_invoice_id,
    #         "origin": original_picking.name,
    #         "picking_id": original_picking.id,
    #         "move_id": self.origin_returned_move_id.id,
    #         "product_id": self.origin_returned_move_id.product_id.id,
    #         "product_uom_qty": self.product_uom_qty,
    #         "product_uom": self.product_uom.id,
    #         "reception_move_id": self.id,
    #         "company_id": self.company_id.id,
    #         "location_id": self.location_dest_id.id,
    #         "state": "confirmed",
    #     }


# class StockRule(models.Model):
#     _inherit = "stock.rule"
#
#     def _get_custom_move_fields(self):
#         return super()._get_custom_move_fields() + ["rma_id"]
#
#
# class StockMoveLine(models.Model):
#     _inherit = "stock.move.line"
#
#     reserved_qty = fields.Float(
#         'Real Reserved Quantity', digits=0, copy=False,
#         compute='_compute_reserved_qty', inverse='_set_reserved_qty', store=True)
#     reserved_uom_qty = fields.Float(
#         'Reserved', default=0.0, digits='Product Unit of Measure', required=True, copy=False)
#     qty_done = fields.Float(compute='_compute_qty_done', inverse='_inverse_qty_done',store =True)
#
#     @api.depends('product_id', 'product_uom_id', 'reserved_uom_qty')
#     def _compute_reserved_qty(self):
#         for line in self:
#             line.reserved_qty = line.product_uom_id._compute_quantity(line.reserved_uom_qty, line.product_id.uom_id,
#                                                                       rounding_method='HALF-UP')
#
#     def _set_reserved_qty(self):
#         """ The meaning of reserved_qty field changed lately and is now a functional field computing the quantity
#         in the default product UoM. This code has been added to raise an error if a write is made given a value
#         for `reserved_qty`, where the same write should set the `reserved_uom_qty` field instead, in order to
#         detect errors. """
#         raise UserError(
#             _('The requested operation cannot be processed because of a programming error setting the `reserved_qty` field instead of the `reserved_uom_qty`.'))